#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>
#include <stdint.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <ctype.h>

#include "platform.h"
#include "../utils/utils.h"
#include "../main/benchmark.h"
#include "../modules/pbmemory.h"
#include "../modules/pbalgorithm.h"
#include "../modules/pbthreads.h"
#include "../modules/pbnet.h"
#include "../modules/pbcpu.h"




static void RemoveNL(char *buff)
{
    int l = (int)strlen(buff);
    
    if (l && buff[l-1] == '\n')
        buff[l-1] = '\0';        
}


static char *GetCPUInfo(char *out, const char *name, const char *default_value)
{
    strcpy(out, default_value);
        
    FILE *f = fopen("/proc/cpuinfo", "rt");
    
    if (!f)
        return out;
        
        
    char buff[1024] = "";
    
    while (fgets(buff, 1023, f))
    {
        if (strstr(buff, name))
        {
            RemoveNL(buff);
            const char *p = strchr(buff, ':') + 1;
            
            while (isspace(p[0]))
                p++;
            
            strcpy(out, p);
            return out;
        }
    }

    return out;
}


double os_GetTime()
{
    struct timeval t;
    
    gettimeofday(&t, NULL);  

    return t.tv_sec + t.tv_usec / 1.e6;        
}


uint64_t os_GetPhysicalMemory()
{   
    FILE *f = fopen("/proc/meminfo", "rt");
    
    if (!f)
        return 0;
        
    char buff[1024] = "", units[32] = "";
    uint64_t totalmem = 0;
    
    while (fgets(buff, 1023, f))
    {
        if (strstr(buff, "MemTotal"))
        {            
            sscanf(buff, "%s%"SCNu64"%s", units, &totalmem, units);
            
            units[0] = toupper(units[0]);
            
            if (units[0] == 'K')
                totalmem *= 1024;
            else if (units[0] == 'M')
                totalmem *= 1024*1024;                
            else if (units[0] == 'G')
                totalmem *= 1024*1024*1024;
                
            return totalmem;            
        }
    }
    
    return 0;        
}


void os_GetOSName(CChString *out)
{
    *out = "Linux";
    
    FILE *f = fopen("/proc/version_signature", "rt");
    
    if (!f)
        return ;
        
    char buff[1024];    
    fgets(buff, 1023, f);
    RemoveNL(buff);
    
    *out = buff;
}


void os_GetCRuntime(CChString *out)
{
    out->Clear();
    
    *out = "unknown";

    *out += ft("; ptr: %d byte", (int)sizeof(void*));
}


void os_GetCompiler(CChString *out)
{
    out->Clear();

#ifdef __GNUC__

    *out += __VERSION__;

#ifdef __EXCEPTIONS
    *out += ", EH";
#else
    *out += ", no EH";
#endif

#ifdef __USING_SJLJ_EXCEPTIONS__
    *out += ", old SJLJ EH";
#endif

#ifdef __SSP__
    *out += ", stack protector";
#endif

#ifdef __SSP_ALL__
    *out += ", stack protector all";
#endif

#endif //__GNUC__
}


int os_GetCPUClockMhz()
{
    double start = os_GetTime();
    
    while (os_GetTime() - start < 1.5)      // lehet, hogy felgozzel megy... megprobaljuk felporgetni :)
        ;
            
    char buff[1024];
    return atoi(GetCPUInfo(buff, "cpu MHz", "0"));
}


void os_GetHw(CChString *out)
{
    char buff[512], hw[1024];
    sprintf(hw, "%s; %dMb RAM; %dMHz", GetCPUInfo(buff, "model name", "unknown cpu"), (int)(os_GetPhysicalMemory() / (1024*1024)), os_GetCPUClockMhz());
    *out = hw;
}


// 
// szalak
//


void *os_CreateThreadVariables(int count)
{
    pthread_t *ah = new pthread_t[count];

    for (int i=0; i<count; i++)
        ah[i] = 0;

    return ah;
}


void os_DeleteThreadVariables(void *&thread_variables, int count)
{
    if (!thread_variables)
        return;

    pthread_t *&threads = (pthread_t*&)thread_variables;

    /*
    for (int i=0; i<count; i++)
    {
        if (threads[i] != 0)
            CloseHandle(threads[i]);
    }
    */

    delete[] threads;
    threads = NULL;
}                          


static void *StartThreadHelper(void *thread)         // a pthread void fuggvenyt akar hivni, ezert itt is azt kell...
{
    ((THREADFUNC)thread)(NULL);
    pthread_exit(NULL);    
}


void os_StartThread(void *thread_variables, int i, THREADFUNC thread, void *thread_param)
{
    pthread_attr_t attr;
    
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    
    pthread_t thread_id = 0;
    
    int ret = pthread_create(&thread_id, NULL, StartThreadHelper, (void*)thread);
    
    if (ret != 0)
    {
        printf("Hiba a %d. szal letrehozasakor", i);
        return;
    }    
    else
        ((pthread_t*)thread_variables)[i] = thread_id;
        
    pthread_attr_destroy(&attr);
}


void os_JoinThread(void *thread_variables, int i)
{
    pthread_t &h = ((pthread_t*)thread_variables)[i];

    if (h != 0)
        pthread_join(h, NULL);
}


void os_CloseThread(void *thread_variables, int i)
{
    pthread_t &h = ((pthread_t*)thread_variables)[i];

    if (h != 0)
    {
        pthread_join(h, NULL);
        h = 0;
    }
}


//
// net
//

bool os_NetInit()
{     
    return true;
}


void os_NetCleanup()
{
}

